﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Dtd.Schema;
using System.Diagnostics;

namespace Engine.Sgml.Parser {
    internal sealed class CDataSectionReader : IElementReader {

        public SElement Read(SgmlStreamReader reader, SElement parent, Context context) {
            reader.SkipWhitespace();
            string name = context.NameReader.ScanNameToken(reader);
            if (name == DName.CData) {
                if (reader.SkipWhitespace() == Characters.LeftBracket)
                    return ReadCDataSection(reader, parent, context, name);
                else {
                    context.TraceSource.TraceEvent(TraceEventType.Error, 0, "Expecting CDATA section. {0}.", reader.Context());
                    IElementReader elementReader = context.Readers[SgmlNodeType.CData];
                    return elementReader.Read(reader, parent, context);
                    }
                }
            else
                return ReadConditionalBlock(reader, parent, context, name);
            }

        private static SElement ReadCDataSection(SgmlStreamReader reader, SElement parent, Context context, string name) {
            SElement cdata = parent.Nodes.Push();
            cdata.Clear();
            cdata.Parent = parent;
            cdata.LineNumber = context.LineNumber;
            cdata.LinePosition = context.LinePosition;
            cdata.Name = name;
            cdata.NodeType = SgmlNodeType.CDataSection;
            if (parent.HasDeclaration && parent.Declaration.ContentModel.Groups.Count > 0)
                cdata.DName = parent.Declaration.ContentModel.Groups.FirstItem.Name;
            else
                cdata.DName = DName.CData;

            reader.SkipWhitespace();
            cdata.Value = reader.ReadToEnd("]]>");
            if (reader.EndOfStream)
                context.TraceSource.TraceEvent(TraceEventType.Error, 0, "CDATA Section starting on {0} was not closed.", cdata.Context());
            // Consume last char in termination sequence.
            reader.ReadChar();
            return cdata;
            }

        public SElement ReadConditionalBlock(SgmlStreamReader reader, SElement parent, Context context, string name) {
            SElement conditional = parent.Nodes.Push();
            conditional.Clear();
            conditional.Parent = parent;
            conditional.LineNumber = context.LineNumber;
            conditional.LinePosition = context.LinePosition;
            conditional.Name = name;
            conditional.DName = InclusionNode.Instance.Name;
            conditional.NodeType = SgmlNodeType.Condition;
            if (reader.CurrentChar == Characters.RightBracket)
                reader.SkipToEnd(new[] { Characters.EndTagMarkup, Characters.StartTagMarkup });
            else
                conditional.Value = reader.ReadToEnd("]>");
            if (reader.EndOfStream)
                context.TraceSource.TraceEvent(TraceEventType.Error, 0, "Conditional Block starting on {0} was not closed.", conditional.Context());
            if (reader.CurrentChar == Characters.EndTagMarkup)
                reader.ReadChar();
            return conditional;
            }
        }
    }
